/**
  ******************************************************************************
  * @file    uart_func.c
  * @author  Firmware Worker
  * @brief   UART communication related functions
  *
  * \license The MIT License (MIT)
  * 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
  * 
  * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
  * 
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "uart_func.h"
#include "debug.h"
#include "ring_buffer.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* UART handler declaration */
UART_HandleTypeDef Uart1Handle;
uint8_t uart1TxData[RING_BUF_SIZE] = {0};
uint8_t uart1RxData[RING_BUF_SIZE] = {0};

tRingBuf uart1TxBuf = {
#if RECORD_LOST_NUM
  .lostNum  = 0,
#endif
  .buf  = uart1TxData,
  .in   = 0,
  .out  = 0,
  .size = RING_BUF_SIZE
};
tRingBuf uart1RxBuf = {
#if RECORD_LOST_NUM
  .lostNum  = 0,
#endif
  .buf  = uart1RxData,
  .in   = 0,
  .out  = 0,
  .size = RING_BUF_SIZE
};

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

void vInitUart1(void)
{
  /* Put the USART peripheral in the Asynchronous mode (UART Mode) */
  /* UART1 configured as follow:
      - Word Length = 8 Bits
      - Stop Bit = One Stop bit
      - Parity = None
      - BaudRate = 115200 baud
      - Hardware flow control disabled (RTS and CTS signals) */
  Uart1Handle.Instance          = USART1;
  
  Uart1Handle.Init.BaudRate     = 115200;
  Uart1Handle.Init.WordLength   = UART_WORDLENGTH_8B;
  Uart1Handle.Init.StopBits     = UART_STOPBITS_1;
  Uart1Handle.Init.Parity       = UART_PARITY_NONE;
  Uart1Handle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
  Uart1Handle.Init.Mode         = UART_MODE_TX_RX;
  Uart1Handle.Init.OverSampling = UART_OVERSAMPLING_16;
    
  if(HAL_UART_Init(&Uart1Handle) != HAL_OK)
  {
    Error_Handler();
  }
#if USE_ST_UART_IT_PROCESS
  HAL_UART_RxCpltCallback(&Uart1Handle);
#else
  /* Enable the UART Data Register not empty Interrupt */
  __HAL_UART_ENABLE_IT(&Uart1Handle, UART_IT_RXNE);
#endif
}

/**
 * @brief Impelement _putchar() function which is used by printf() function.
 * 
 * @param character one print character
 */
void _putchar(char character)
{
  iPutDataToBuf(&uart1TxBuf, (uint8_t*)&character, 1);
#if USE_ST_UART_IT_PROCESS
  static uint8_t data;
  if (Uart1Handle.gState == HAL_UART_STATE_READY)
  {
    if(iGetDataFromBuf(&uart1TxBuf, &data, 1))
      HAL_UART_Transmit_IT(&Uart1Handle, &data, 1);
  }
#else
    // Enable TX transmission interrupt
    SET_BIT(Uart1Handle.Instance->CR1, USART_CR1_TXEIE);
#endif
}

/**
 * @brief get one character from UART RX buffer.
 * 
 * @param None
 * @retval one character
 */
char _getchar(void)
{
  char character = 0;
  iGetDataFromBuf(&uart1RxBuf, (uint8_t*)&character, 1);
  return character;
}

#if USE_ST_UART_IT_PROCESS
/**
  * @brief  Tx Transfer completed callbacks.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
  static uint8_t character;
  if (Uart1Handle.gState == HAL_UART_STATE_READY)
  {
    if(iGetDataFromBuf(&uart1TxBuf, &character, 1))
      HAL_UART_Transmit_IT(&Uart1Handle, &character, 1);
  }
}

/**
  * @brief  Rx Transfer completed callbacks.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  static uint8_t startRecv = 1;
  static uint8_t character;
  /* Check that a Rx process is not already ongoing */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if(startRecv == 0) {
      iPutDataToBuf(&uart1RxBuf, (uint8_t*)&character, 1);
    } else {
      startRecv = 0;
    }
    HAL_UART_Receive_IT(huart, &character, 1);
  }
}
#else
void UartIrqService(UART_HandleTypeDef *huart)
{
  uint32_t SR = huart->Instance->SR;
  uint32_t CR1 = huart->Instance->CR1;
  uint32_t CR3 = huart->Instance->CR3;
  tRingBuf *pTxBuf = &uart1TxBuf;
  tRingBuf *pRxBuf = &uart1RxBuf;

  UNUSED(CR3);

  if (huart == &Uart1Handle)
  {
    pTxBuf = &uart1TxBuf;
    pRxBuf = &uart1RxBuf;
  }

  while (SR & USART_SR_RXNE)
  {
#if RECORD_LOST_NUM
    if (SR & USART_SR_ORE)
    {
      pRxBuf->lostNum++;
    }
#endif

    if (((pRxBuf->in + 1) % pRxBuf->size) != pRxBuf->out)
    {
      pRxBuf->buf[pRxBuf->in++] = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
      pRxBuf->in %= pRxBuf->size;
    }
    else
    {
      pRxBuf->buf[pRxBuf->in] = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
#if RECORD_LOST_NUM
      pRxBuf->lostNum++;
#endif
    }

    SR = huart->Instance->SR;
  }

  if ((SR & USART_SR_TXE) && (CR1 & USART_CR1_TXEIE))
  {
    if (pTxBuf->in != pTxBuf->out)
    {
      huart->Instance->DR = (uint8_t)(pTxBuf->buf[pTxBuf->out++] & (uint8_t)0x00FF);
      pTxBuf->out %= pTxBuf->size;
    }
    else
    {
      CLEAR_BIT(huart->Instance->CR1, USART_CR1_TXEIE);
    }
  }
}
#endif
